package com.ruoyi.business.modbus.serial.jserialcomm;

import com.fazecast.jSerialComm.SerialPort;
import com.serotonin.modbus4j.serial.SerialPortWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.io.OutputStream;

/**
 * 基于jSerialComm的SerialPortWrapper实现
 * 
 * @author ruoyi
 * @date 2024-01-01
 */
public class JSerialCommSerialPortWrapper implements SerialPortWrapper {
    
    private static final Logger log = LoggerFactory.getLogger(JSerialCommSerialPortWrapper.class);
    
    private SerialPort serialPort;
    private final String commPortId;
    private final int baudRate;
    private final int dataBits;
    private final int stopBits;
    private final int parity;
    
    private JSerialCommInputStream inputStream;
    private JSerialCommOutputStream outputStream;
    
    /**
     * 构造函数
     * 
     * @param commPortId 串口ID (如: COM1, /dev/ttyUSB0)
     * @param baudRate 波特率
     * @param dataBits 数据位
     * @param stopBits 停止位
     * @param parity 校验位
     */
    public JSerialCommSerialPortWrapper(String commPortId, int baudRate, int dataBits, int stopBits, int parity) {
        this.commPortId = commPortId;
        this.baudRate = baudRate;
        this.dataBits = dataBits;
        this.stopBits = stopBits;
        this.parity = parity;
        
        log.info("创建jSerialComm串口包装器: 端口={}, 波特率={}, 数据位={}, 停止位={}, 校验位={}", 
                 commPortId, baudRate, dataBits, stopBits, parity);
    }
    
    @Override
    public void close() throws Exception {
        log.debug("关闭串口: {}", commPortId);
        
        try {
            // 关闭输入输出流
            if (inputStream != null) {
                inputStream.close();
                inputStream = null;
            }
            if (outputStream != null) {
                outputStream.close();
                outputStream = null;
            }
            
            // 关闭串口
            if (serialPort != null && serialPort.isOpen()) {
                boolean closed = serialPort.closePort();
                if (closed) {
                    log.info("串口 {} 已关闭", commPortId);
                } else {
                    log.warn("串口 {} 关闭失败", commPortId);
                }
            }
        } catch (Exception e) {
            log.error("关闭串口失败: {}", e.getMessage());
            throw new Exception("关闭串口失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public void open() throws Exception {
        log.debug("打开串口: {}", commPortId);
        
        try {
            // 获取串口对象
            serialPort = SerialPort.getCommPort(commPortId);
            
            if (serialPort == null) {
                throw new Exception("无法获取串口: " + commPortId);
            }
            
            // 设置串口参数
            serialPort.setBaudRate(baudRate);
            serialPort.setNumDataBits(dataBits);
            serialPort.setNumStopBits(convertStopBits(stopBits));
            serialPort.setParity(convertParity(parity));
            
            // 设置超时时间
            serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING | SerialPort.TIMEOUT_WRITE_BLOCKING, 1000, 0);
            
            // 打开串口
            boolean opened = serialPort.openPort();
            if (!opened) {
                throw new Exception("无法打开串口: " + commPortId);
            }
            
            log.info("串口 {} 打开成功", commPortId);
            
            // 创建输入输出流
            inputStream = new JSerialCommInputStream(serialPort);
            outputStream = new JSerialCommOutputStream(serialPort);
            
        } catch (Exception e) {
            log.error("打开串口失败: {}", e.getMessage());
            throw new Exception("打开串口失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public InputStream getInputStream() {
        if (inputStream == null) {
            throw new IllegalStateException("串口未打开或输入流未初始化");
        }
        return inputStream;
    }
    
    @Override
    public OutputStream getOutputStream() {
        if (outputStream == null) {
            throw new IllegalStateException("串口未打开或输出流未初始化");
        }
        return outputStream;
    }
    
    @Override
    public int getBaudRate() {
        return baudRate;
    }
    
    @Override
    public int getDataBits() {
        return dataBits;
    }
    
    @Override
    public int getStopBits() {
        return stopBits;
    }
    
    @Override
    public int getParity() {
        return parity;
    }
    
    /**
     * 转换停止位参数
     */
    private int convertStopBits(int stopBits) {
        switch (stopBits) {
            case 1: return SerialPort.ONE_STOP_BIT;
            case 2: return SerialPort.TWO_STOP_BITS;
            default:
                log.warn("不支持的停止位: {}, 使用默认值1", stopBits);
                return SerialPort.ONE_STOP_BIT;
        }
    }
    
    /**
     * 转换校验位参数
     */
    private int convertParity(int parity) {
        switch (parity) {
            case 0: return SerialPort.NO_PARITY;
            case 1: return SerialPort.ODD_PARITY;
            case 2: return SerialPort.EVEN_PARITY;
            case 3: return SerialPort.MARK_PARITY;
            case 4: return SerialPort.SPACE_PARITY;
            default:
                log.warn("不支持的校验位: {}, 使用默认值NONE", parity);
                return SerialPort.NO_PARITY;
        }
    }
    
    /**
     * 获取串口状态信息
     */
    public String getPortInfo() {
        return String.format("Port[%s] BaudRate[%d] DataBits[%d] StopBits[%d] Parity[%d] Opened[%s]",
                commPortId, baudRate, dataBits, stopBits, parity, 
                serialPort != null ? serialPort.isOpen() : false);
    }
    
    /**
     * 获取底层串口对象
     */
    public SerialPort getSerialPort() {
        return serialPort;
    }
    
    /**
     * 设置读取超时时间
     * 
     * @param timeoutMs 超时时间(毫秒)
     */
    public void setReadTimeout(int timeoutMs) {
        if (serialPort != null && serialPort.isOpen()) {
            serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING | SerialPort.TIMEOUT_WRITE_BLOCKING, timeoutMs, 0);
            log.debug("设置读取超时时间: {}ms", timeoutMs);
        }
    }
    
    /**
     * 清空输入输出缓冲区
     */
    public void flushIOBuffers() {
        if (serialPort != null && serialPort.isOpen()) {
            serialPort.flushIOBuffers();
            log.debug("已清空输入输出缓冲区");
        }
    }
}
